package math_utils

import (
	"game/app/systems/math_system/math_defines"
	"math"
)

type ZNumber struct {
	Value int64
}

func MakeZNumberByInt64(v int64) ZNumber {
	return ZNumber{
		Value: math_defines.AccuracyValue * v,
	}
}

func MakeZNumberByInt32(v int32) ZNumber {
	return ZNumber{
		Value: int64(math_defines.AccuracyValue * v),
	}
}

func MakeZNumberByFloat64(f float64) ZNumber {
	return ZNumber{
		Value: int64(math_defines.AccuracyValue * f),
	}
}

func MakeZNumberByFloat32(f float32) ZNumber {
	return ZNumber{
		Value: int64(math_defines.AccuracyValue * f),
	}
}

func MakeByReal(v int64) ZNumber {
	return ZNumber{
		Value: v,
	}
}

func (z ZNumber) Add(other ZNumber) ZNumber {
	res := ZNumber{}
	res.Value = z.Value + other.Value
	return res
}

func (z ZNumber) Sub(other ZNumber) ZNumber {
	res := ZNumber{}
	res.Value = z.Value - other.Value
	return res
}

func (z ZNumber) Mul(other ZNumber) ZNumber {
	res := ZNumber{}
	res.Value = z.Value * other.Value / math_defines.AccuracyValue
	return res
}

func (z ZNumber) Dlv(other ZNumber) ZNumber {
	res := ZNumber{}
	res.Value = z.Value * math_defines.AccuracyValue / other.Value
	return res
}

func (z ZNumber) Sqrt() ZNumber {
	res := ZNumber{}
	res.Value = int64(math.Sqrt(float64(z.Value) * math_defines.AccuracyValue))
	return res
}

func (z ZNumber) ToFloat32() float32 {
	value := float32(z.Value)
	return value / math_defines.AccuracyValue
}

func (z ZNumber) ToFloat64() float64 {
	value := float64(z.Value)
	return value / math_defines.AccuracyValue
}

func (z ZNumber) ToInt32() int32 {
	return int32(z.Value / math_defines.AccuracyValue)
}

func (z ZNumber) ToInt64() int64 {
	return z.Value / math_defines.AccuracyValue
}

func (z ZNumber) ToGrid() int32 {
	if z.Value < 0 {
		return math_defines.InvalidGridIndex
	}

	value := z.Value / math_defines.AccuracyValue / math_defines.GridSize
	return int32(value)
}

func (z ZNumber) Ceil(other ZNumber) ZNumber {
	if other.Value <= 0 {
		return ZNumber{}
	}

	res := ZNumber{}
	res.Value = (z.Value + other.Value - 1) / other.Value * other.Value
	return res
}

func (z ZNumber) Floor(other ZNumber) ZNumber {
	if other.Value <= 0 {
		return ZNumber{}
	}

	res := ZNumber{}
	res.Value = z.Value / other.Value * other.Value
	return res
}

func (z ZNumber) IsZero() bool {
	return z.Value == 0
}

func (z ZNumber) Abs() ZNumber {
	if z.Value > 0 {
		return ZNumber{Value: z.Value}
	} else {
		return ZNumber{Value: -z.Value}
	}
}

// >
func (z ZNumber) Me(other ZNumber) bool {
	return z.Value > other.Value
}

// >
func (z ZNumber) IsMe(o int64) bool {
	return z.Value > o*math_defines.AccuracyValue
}

// >=
func (z ZNumber) Mq(other ZNumber) bool {
	return z.Value >= other.Value
}

// <
func (z ZNumber) Le(other ZNumber) bool {
	return z.Value < other.Value
}

// <
func (z ZNumber) IsLe(o int64) bool {
	return z.Value < o*math_defines.AccuracyValue
}

// <=
func (z ZNumber) IsLq(o int64) bool {
	return z.Value <= o*math_defines.AccuracyValue
}

// <=
func (z ZNumber) Lq(other ZNumber) bool {
	return z.Value <= other.Value
}

// ==
func (z ZNumber) Eq(other ZNumber) bool {
	return z.Value == other.Value
}

func (z ZNumber) IsEq(o int64) bool {
	return z.Value == o*math_defines.AccuracyValue
}

// !=
func (z ZNumber) Ne(other ZNumber) bool {
	return z.Value != other.Value
}

// !=
func (z ZNumber) IsNe(o int64) bool {
	return z.Value != o*math_defines.AccuracyValue
}

func (z ZNumber) Neg() ZNumber {
	res := ZNumber{}
	res.Value = -z.Value
	return res
}

func FloatSqrt(other ZNumber) ZNumber {
	return other.Sqrt()
}

func Swap(a *ZNumber, b *ZNumber) {
	*b, *a = *a, *b
}
